#include "PSFDetectorConstruction.hh"

#include "PSFDetectorMessenger.hh"
#include "PSFMaterials.hh"
#include "PSFPhotonDetSD.hh"

#include "globals.hh"
#include "G4Box.hh"
#include "G4EllipticalTube.hh"
#include "G4ios.hh"
#include "G4GeometryManager.hh"
#include "G4LogicalBorderSurface.hh"
#include "G4LogicalSkinSurface.hh"
#include "G4LogicalVolume.hh"
#include "G4LogicalVolumeStore.hh"
#include "G4Material.hh"
#include "G4NistManager.hh"
#include "G4OpticalSurface.hh"
#include "G4PhysicalConstants.hh"
#include "G4PhysicalVolumeStore.hh"
#include "G4PVPlacement.hh"
#include "G4RunManager.hh"
#include "G4SDManager.hh"
#include "G4SolidStore.hh"
#include "G4SystemOfUnits.hh"
#include "G4Tubs.hh"
#include "G4UserLimits.hh"

#include "G4VisAttributes.hh"
#include "G4Colour.hh"

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

PSFDetectorConstruction::PSFDetectorConstruction()
    : fVisAttributes()
{
  fDetectorMessenger = new PSFDetectorMessenger(this);

  fMPPCHalfL = fPSFCoreRY;
  fClrfiberZ = fMPPCZ * mm;
  fHoleLength = fBarLength;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

PSFDetectorConstruction::~PSFDetectorConstruction()
{
  delete fDetectorMessenger;
  delete fMaterials;
  for (auto visAttributes : fVisAttributes)
  {
    delete visAttributes;
  }
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4VPhysicalVolume *PSFDetectorConstruction::Construct()
{
  if (fPhysiWorld)
  {
    G4GeometryManager::GetInstance()->OpenGeometry();
    G4PhysicalVolumeStore::GetInstance()->Clean();
    G4LogicalVolumeStore::GetInstance()->Clean();
    G4SolidStore::GetInstance()->Clean();
    G4LogicalSkinSurface::CleanSurfaceTable();
    G4LogicalBorderSurface::CleanSurfaceTable();
  }

  fMaterials = PSFMaterials::GetInstance();
  UpdateGeometryParameters();

  return ConstructDetector();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4VPhysicalVolume *PSFDetectorConstruction::ConstructDetector()
{
  auto Polystyrene = FindMaterial("Polystyrene");
  auto fpethylene = FindMaterial("FPethylene");
  auto pethylene = FindMaterial("Pethylene");
  auto silicone = FindMaterial("Silicone");
  auto pmma = FindMaterial("PMMA");
  auto aluminum = FindMaterial("G4_Al");
  auto air = FindMaterial("G4_AIR");
  G4NistManager *nist = G4NistManager::Instance();
  G4Material *pb = nist->FindOrBuildMaterial("G4_Pb");
  G4Material *fe = nist->FindOrBuildMaterial("G4_Fe");
  G4Material *al = nist->FindOrBuildMaterial("G4_Al");
  // G4cout << "\nMaterial Properties Table for G4_AIR:" << G4endl;
  // air->GetMaterialPropertiesTable()->DumpTable();

  //--------------------------------------------------
  // World 世界
  //--------------------------------------------------

  G4VSolid *solidWorld =
      new G4Box("World", 10.0 * m, 10.0 * m, 10.0 * m);
  fLogicWorld =
      new G4LogicalVolume(solidWorld, air, "World");

  fPhysiWorld =
      new G4PVPlacement(nullptr, G4ThreeVector(), fLogicWorld, "World", nullptr,
                        false, 0);

  //--------------------------------------------------
  // Fiber Construction 光纤
  //--------------------------------------------------

  // Boundary Surface Properties
  // 光纤表面属性
  G4OpticalSurface *opSurface = nullptr;

  if (fSurfaceRoughness < 1.)
    opSurface = new G4OpticalSurface("RoughSurface", glisur, ground,
                                     dielectric_dielectric, fSurfaceRoughness);

  G4LogicalVolume *logicPSFCore = nullptr;
  G4LogicalVolume *logicClad1 = nullptr;
  G4LogicalVolume *logicClad2 = nullptr;
  G4VPhysicalVolume *physiClad1 = nullptr;
  G4VPhysicalVolume *physiClad2 = nullptr;

  // Determine the number of cladding layers to be built
  // 光纤的包层数
  // 0: no cladding, 1: 1 cladding, 2: 2 claddings

  //--------------------------------------------------
  // Cladding 1
  //--------------------------------------------------

  G4cout << "\nMaterial Properties Table for pmma:" << G4endl;
  pmma->GetMaterialPropertiesTable()->DumpTable();

  G4VSolid *solidClad1;

  if (fXYRatio == 1.) // 圆柱
    solidClad1 = new G4Tubs("Clad1", 0., fClad1RY, fClad1Z, 0., twopi);
  else // 椭圆柱
    solidClad1 = new G4EllipticalTube("Clad1", fClad1RX, fClad1RY, fClad1Z);

  logicClad1 = new G4LogicalVolume(solidClad1, pmma, "Clad1");

  //--------------------------------------------------
  // PSFCore 光纤核心
  //--------------------------------------------------

  G4cout << "\nMaterial Properties Table for Polystyrene:" << G4endl;
  Polystyrene->GetMaterialPropertiesTable()->DumpTable();

  G4VSolid *solidPSFCore;
  G4VPhysicalVolume *physiPSFCore;

  if (fXYRatio == 1.) // 圆柱
  {
    solidPSFCore = new G4Tubs("PSFCore", 0., fPSFCoreRY, fPSFCoreZ, 0., twopi);
  }
  else // 椭圆柱
  {
    solidPSFCore = new G4EllipticalTube("PSFCore", fPSFCoreRX, fPSFCoreRY, fPSFCoreZ);
  }

  logicPSFCore = new G4LogicalVolume(solidPSFCore, Polystyrene, "PSFCore");

  logicPSFCore->SetUserLimits(new G4UserLimits(DBL_MAX, DBL_MAX, 10. * ms));

  //--------------------------------------------------
  // Clear Fiber (Coupling Layer)Couple
  //--------------------------------------------------
  //
  //
  // 硅树脂
  G4VSolid *solidCouple = new G4Tubs("ClearFiber", 0., fClad1RY, fClrfiberZ, 0., twopi);
  auto logicCouple = new G4LogicalVolume(solidCouple, silicone, "ClearFiber");

  //--------------------------------------------------
  // PhotonDet 灵敏探测器
  //--------------------------------------------------

  // Physical Construction
  G4VSolid *solidPhotonDet = nullptr;
  G4VPhysicalVolume *physiPhotoDet = nullptr;
  if (fMPPCShape == "Square") // 立方体
  {
    solidPhotonDet = new G4Box("PhotonDet", fClad1RY, fClad1RY, fMPPCZ);
  }
  else // 圆柱
  {
    solidPhotonDet = new G4Tubs("PhotonDet", 0., fClad1RY, fMPPCZ, 0., twopi);
  }

  auto logicPhotonDet = new G4LogicalVolume(solidPhotonDet, aluminum, "PhotonDet_LV");

  // 放置涂层
  physiClad1 = new G4PVPlacement(nullptr, G4ThreeVector(0., 0., 0.), logicClad1, "Clad1", fLogicWorld, false, 1);
  // 放置硅树脂
  new G4PVPlacement(nullptr, G4ThreeVector(0., 0., fCoupleOrigin), logicCouple, "ClearFiber", fLogicWorld, false, 1);
  // new G4PVPlacement(nullptr, G4ThreeVector(0., 0., -fCoupleOrigin), logicCouple, "ClearFiber", fLogicWorld, false, 2);
  // 放置光纤核心
  physiPSFCore = new G4PVPlacement(nullptr, G4ThreeVector(0., 0., 0.), logicPSFCore, "PSFCore", logicClad1, false, 1);
  // 放置探测器
  physiPhotoDet = new G4PVPlacement(nullptr, G4ThreeVector(0., 0., fPSFCoreZ + 2 * fClrfiberZ + fMPPCZ), logicPhotonDet, "PhotonDet", fLogicWorld, false, 1);
  // physiPhotoDet = new G4PVPlacement(nullptr, G4ThreeVector(0., 0., -(fPSFCoreZ + 2*fClrfiberZ + fMPPCZ)), logicPhotonDet, "PhotonDet", fLogicWorld, false, 2);

  // PhotonDet Surface Properties
  auto photonDetSurface = new G4OpticalSurface("PhotonDetSurface", glisur, ground, dielectric_metal, fMPPCPolish);

  auto photonDetSurfaceProperty = new G4MaterialPropertiesTable();

  std::vector<G4double> p_mppc = {2.00 * eV, 3.47 * eV};
  std::vector<G4double> refl_mppc = {fMPPCReflectivity, fMPPCReflectivity};
  std::vector<G4double> effi_mppc = {1., 1.};

  photonDetSurfaceProperty->AddProperty("REFLECTIVITY", p_mppc, refl_mppc); // 反射率
  photonDetSurfaceProperty->AddProperty("EFFICIENCY", p_mppc, effi_mppc);   // 效率

  photonDetSurface->SetMaterialPropertiesTable(photonDetSurfaceProperty);

  new G4LogicalSkinSurface("PhotonDetSurface", logicPhotonDet, photonDetSurface);

  // Place the rough surface only if needed
  if (opSurface)
  {
    new G4LogicalBorderSurface("surfaceClad1Out", physiClad1, fPhysiWorld, opSurface);

    new G4LogicalBorderSurface("surfaceClad1In", fPhysiWorld, physiClad1, opSurface);

    new G4LogicalBorderSurface("surfacePSFOut", physiPSFCore, physiClad1, opSurface);

    new G4LogicalBorderSurface("surfacePSFIn", physiClad1, physiPSFCore, opSurface);
  }
  // visualization attributes -------------------------------------------------

  auto visAttributes = new G4VisAttributes(G4Colour(1.0, 1.0, 1.0));
  visAttributes->SetVisibility(false);
  fLogicWorld->SetVisAttributes(visAttributes);
  fVisAttributes.push_back(visAttributes);

  if (logicClad1 != nullptr)
  {
    visAttributes = new G4VisAttributes(G4Colour(0.0, 0.8, 0.5, 0.5));
    visAttributes->SetVisibility(true);
    logicClad1->SetVisAttributes(visAttributes);
    fVisAttributes.push_back(visAttributes);
  }

  visAttributes = new G4VisAttributes(G4Colour(0.8, 0.8, 1.0));
  visAttributes->SetVisibility(true);
  logicPSFCore->SetVisAttributes(visAttributes);
  fVisAttributes.push_back(visAttributes);

  visAttributes = new G4VisAttributes(G4Colour(0.0, 0.0, 0.5, 0.5));
  visAttributes->SetVisibility(true);
  logicCouple->SetVisAttributes(visAttributes);
  fVisAttributes.push_back(visAttributes);

  visAttributes = new G4VisAttributes(G4Colour(1.0, 0.0, 0.0, 0.5));
  visAttributes->SetVisibility(true);
  logicPhotonDet->SetVisAttributes(visAttributes);
  fVisAttributes.push_back(visAttributes);

  return fPhysiWorld;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::ConstructSDandField()
{
  if (!fmppcSD.Get())
  {
    G4String mppcSDName = "PSF/PhotonDet";
    auto mppcSD = new PSFPhotonDetSD(mppcSDName);
    G4SDManager::GetSDMpointer()->AddNewDetector(mppcSD);
    fmppcSD.Put(mppcSD);
  }
  SetSensitiveDetector("PhotonDet_LV", fmppcSD.Get(), true);
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::UpdateGeometryParameters()
{
  fPSFCoreRX = fXYRatio * fPSFCoreRY;

  fClad1RX = fPSFCoreRX + fClad1RY - fPSFCoreRY;
  fClad1Z = fPSFCoreZ;

  fMPPCOriginZ = fPSFCoreZ + fMPPCZ;
  fCoupleOrigin = fPSFCoreZ + fClrfiberZ;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetPhotonDetGeometry(G4String shape)
// Set the Geometry of the PhotonDet detector
// Pre:  shape must be either "Circle" and "Square"
{
  if (shape == "Circle" || shape == "Square")
    fMPPCShape = shape;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetNumberOfCladding(G4int num)
// Set the number of claddings
// Pre: 0 <= num <= 2
{
  fNumOfCladLayers = num;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetPSFLength(G4double length)
// Set the TOTAL length of the PSF
{
  fPSFCoreZ = length;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetPSFCoreRadius(G4double radius)
// Set the Y radius of PSFCore fiber
{
  fPSFCoreRY = radius;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetClad1Radius(G4double radius)
// Set the Y radius of Cladding 1
{
  fClad1RY = radius;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetClad2Radius(G4double radius)
// Set the Y radius of Cladding 2
{
  fClad2RY = radius;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetPhotonDetHalfLength(G4double halfL)
// Set the half length of the PhotonDet detector
// The half length will be the radius if PhotonDet is circular
{
  fMPPCHalfL = halfL;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetGap(G4double gap)
// Set the distance between fiber end and PhotonDet
{
  fMPPCDist = gap;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetPhotonDetAlignment(G4double theta)
// Set the Aligment of PhotonDet with respect to the z axis
// If theta is 0 deg, then the detector is perfectly aligned
// PhotonDet will be deviated by theta from z axis facing towards the center of the fiber
// 设置PhotonDet相对于z轴的对准
// 如果theta为0度，则探测器完全对准
// PhotonDet将被theta偏离，从z轴朝向光纤中心
{
  fMPPCTheta = theta;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetSurfaceRoughness(G4double roughness)
// Set the Surface Roughness between Cladding 1 and PSFCore fiber
// Pre: 0 < roughness <= 1
{
  fSurfaceRoughness = roughness;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetMirrorPolish(G4double polish)
// Set the Polish of the mirror, polish of 1 is a perfect mirror surface
// Pre: 0 < polish <= 1
// 设置镜子的抛光度，抛光度为1是一个完美的镜面
{
  fMirrorPolish = polish;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetMirrorReflectivity(G4double reflectivity)
// Set the Reflectivity of the mirror, reflectivity of 1 is a perfect mirror
// Pre: 0 < reflectivity <= 1
// 设置镜子的反射率，反射率为1是一个完美的镜子
{
  fMirrorReflectivity = reflectivity;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetPhotonDetPolish(G4double polish)
// Set the Polish of the PhotonDet, polish of 1 is a perfect mirror surface
// Pre: 0 < polish <= 1
// 设置PhotonDet的抛光度，抛光度为1是一个完美的镜面
{
  fMPPCPolish = polish;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetPhotonDetReflectivity(G4double reflectivity)
// Set the Reflectivity of the PhotonDet, reflectivity of 1 is a perfect mirror
// Pre: 0 < reflectivity <= 1
// 设置PhotonDet的反射率，反射率为1是一个完美的镜子
{
  fMPPCReflectivity = reflectivity;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetMirror(G4bool flag)
// Toggle to place the mirror or not at one end (-z end) of the fiber
// True means place the mirror, false means otherwise
// 切换是否在光纤的一端(-z端)放置镜子
{
  fMirrorToggle = flag;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetXYRatio(G4double r)
// Set the ratio of the x and y radius of the ellipse (x/y)
// a ratio of 1 would produce a circle
{
  fXYRatio = r;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetBarLength(G4double length)
// Set the length of the scintillator bar
{
  fBarLength = length;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetBarBase(G4double side)
// Set the side of the scintillator bar
{
  fBarBase = side;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetHoleRadius(G4double radius)
// Set the radius of the fiber hole
{
  fHoleRadius = radius;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetCoatingThickness(G4double thick)
// Set thickness of the coating on the bars
{
  fCoatingThickness = thick;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void PSFDetectorConstruction::SetCoatingRadius(G4double radius)
// Set inner radius of the corner bar coating
{
  fCoatingRadius = radius;
  G4RunManager::GetRunManager()->GeometryHasBeenModified();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4double PSFDetectorConstruction::GetPSFCoreLength() { return fPSFCoreZ; }

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4double PSFDetectorConstruction::GetBarLength() { return fBarLength; }

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4double PSFDetectorConstruction::GetBarBase() { return fBarBase; }

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4double PSFDetectorConstruction::GetHoleRadius() { return fHoleRadius; }

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4double PSFDetectorConstruction::GetHoleLength() { return fHoleLength; }

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4double PSFDetectorConstruction::GetFiberRadius() { return GetPSFRMax(); }

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4double PSFDetectorConstruction::GetCoatingThickness()
{
  return fCoatingThickness;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4double PSFDetectorConstruction::GetCoatingRadius() { return fCoatingRadius; }

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4double PSFDetectorConstruction::GetPSFCoreEnd()
{
  return 0. + fPSFCoreZ;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4double PSFDetectorConstruction::GetPSFRMax()
{
  if (fNumOfCladLayers == 2)
    return fClad2RY;
  if (fNumOfCladLayers == 1)
    return fClad1RY;
  return fPSFCoreRY;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4double PSFDetectorConstruction::GetSurfaceRoughness()
{
  return fSurfaceRoughness;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

// Return True if the fiber construction is ideal
G4bool PSFDetectorConstruction::IsPerfectFiber()
{
  return fSurfaceRoughness == 1. && fXYRatio == 1. &&
         (!fMirrorToggle || (fMirrorPolish == 1. && fMirrorReflectivity == 1.));
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4Material *PSFDetectorConstruction::FindMaterial(G4String name)
{
  G4Material *material = G4Material::GetMaterial(name, true);
  return material;
}